<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>仓库DEMO</title>
    <style>
        html, body {
            margin: 0;
            padding: 0;
            height: 100%;
            overflow: hidden;
        }
    </style>
    <script src="../lib/three.js"></script>
    <script src="../lib/ThreeBSP.js"></script>
    <script src="../lib/OrbitControls.js"></script>
    <script src="../lib/stats.js"></script>
    <script src="../lib/dat.gui.min.js"></script>
    <script src="../lib/FBXLoader.js"></script>
    <script src="../lib/GLTFLoader.js"></script>
    <script src="../lib/tween.js"></script>
</head>
<body>
    <script>
        var scene, camera, renderer, light, controls, stats;

        // 初始化场景
        function initScene() {
            scene = new THREE.Scene();
            // scene.fog = new THREE.Fog(scene.background, 3000, 5000);
        }

        // 初始化相机
        function initCamera() {
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
            camera.position.set(0, 1400, 1400);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
        }

        // 初始化灯光
        function initLight() {
            var directionalLight = new THREE.DirectionalLight(0xffffff, 0.3); // 模拟阳光
            directionalLight.color.setHSL(0.1, 1, 0.95);
            directionalLight.position.set(0, 200, -80).normalize(); // normalize作用：将该向量转换为单位向量（unit vector）， 也就是说，将该向量的方向设置为和原向量相同，但是其长度（length）为1。
            scene.add(directionalLight);

            var ambientLight = new THREE.AmbientLight(0xffffff, 0.8); // 环境光
            // ambientLight.position.set(0, 0, 0);
            scene.add(ambientLight);
        }

        // 初始化渲染器
        function initRenderer() {
            renderer = new THREE.WebGLRenderer({
                antialias: true
            });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setClearColor(0x4682B4, 1.0);
            document.body.appendChild(renderer.domElement);
        }

        // 初始化模型
        function initModels() {
            createFloor();
            createWall();
            createDoor();
            createWindow();
            createArea(-700, 1.5, 0, 400, 600, "库区1号", "#FF0000", 20, "左对齐");
            createArea(0, 1.5, 0, 400, 600, "库区2号", "#FF0000", 20, "左对齐");
            createArea(700, 1.5, 0, 400, 600, "库区3号", "#FF0000", 20, "左对齐");
            createShelf();
            createForklift(initForkliftAnimation);
        }

        // 创建地板
        function createFloor() {
            var loader = new THREE.TextureLoader();
            loader.load("../texture/floor.jpg", function(texture) {
                texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
                texture.repeat.set(10, 10);
                var floorGeometry = new THREE.BoxGeometry(2600, 1400, 1);
                var floorMaterial = new THREE.MeshLambertMaterial({
                    map: texture,
                    side: THREE.DoubleSide
                });
                var floor = new THREE.Mesh(floorGeometry, floorMaterial);
                floor.position.y = -0.5;
                floor.rotation.x = Math.PI / 2;
                floor.name = "地面";
                scene.add(floor);
            });
        }

        // 创建墙壁
        function createWall() {
            const wallMaterial = new THREE.MeshPhongMaterial({color: 0xafc0ca});
            createCube(10, 200, 1400, 0, wallMaterial, -1295, 100, 0, '墙面');
            createCube(10, 200, 1400, 0, wallMaterial, 1295, 100, 0, "墙面");
            createCube(10, 200, 2600, 0.5, wallMaterial, 0, 100, -700, "墙面");
            // 获取前方墙面
            const frontWall = createCube(10, 200, 2600, 0.5, wallMaterial, 0, 100, 700, "墙面", 0, 0, 0, false);
            // 墙面镂空处配置（用于安装门窗）
            const hollows = [
                [200, 180, 10, 0, wallMaterial, -600, 90, 700, "前门1", 0, 0, 0, false],
                [200, 180, 10, 0, wallMaterial, 600, 90, 700, "前门2", 0, 0, 0, false],
                [100, 100, 10, 0, wallMaterial, -900, 90, 700, "窗户1", 0, 0, 0, false],
                [100, 100, 10, 0, wallMaterial, 900, 90, 700, "窗户2", 0, 0, 0, false],
                [100, 100, 10, 0, wallMaterial, -200, 90, 700, "窗户3", 0, 0, 0, false],
                [100, 100, 10, 0, wallMaterial, 200, 90, 700, "窗户4", 0, 0, 0, false]
            ];
            // 生成镂空处的立方体
            const hollowCubes = hollows.map(hollow => createCube.apply(null, hollow));
            // 创建前墙面镂空后的立方体
            createResultBSP(frontWall, hollowCubes);
        }

        // 创建门
        function createDoor() {
            const loader = new THREE.TextureLoader();
            const leftDoorTexture = loader.load('../texture/door_left.png');
            const rightDoorTexture = loader.load('../texture/door_right.png');
            const leftDoorMaterial = new THREE.MeshBasicMaterial({
                map: leftDoorTexture,
                opacity: 1.0,
                transparent: true
            });
            const rightDoorMaterial = new THREE.MeshBasicMaterial({
                map: rightDoorTexture,
                opacity: 1.0,
                transparent: true
            });
            createCube(100, 180, 2, 0, leftDoorMaterial, -700, 90, 700, "左门1", 50);
            createCube(100, 180, 2, 0, rightDoorMaterial, -500, 90, 700, "右门1", -50);
            createCube(100, 180, 2, 0, leftDoorMaterial, 500, 90, 700, "左门2", 50);
            createCube(100, 180, 2, 0, rightDoorMaterial, 700, 90, 700, "右门2", -50);
        }

        // 创建窗户
        function createWindow() {
            const loader = new THREE.TextureLoader();
            const windowTexture = loader.load('../texture/window.png');
            const windowMaterial = new THREE.MeshBasicMaterial({
                map: windowTexture,
                opacity: 1.0,
                transparent: true
            });
            createCube(100, 100, 2, 0, windowMaterial, -900, 90, 700, "窗户");
            createCube(100, 100, 2, 0, windowMaterial, 900, 90, 700, "窗户");
            createCube(100, 100, 2, 0, windowMaterial, -200, 90, 700, "窗户");
            createCube(100, 100, 2, 0, windowMaterial, 200, 90, 700, "窗户");
        }

        // 创建立方体
        function createCube(width, height, depth, angle, material, x, y, z, name, translateX = 0, trnaslateY = 0, translateZ = 0, addToScene = true) {
            var cubeGeometry = new THREE.BoxGeometry(width, height, depth);
            cubeGeometry.translate(translateX, trnaslateY, translateZ);
            var cube = new THREE.Mesh(cubeGeometry, material);
            cube.position.set(x, y, z);
            cube.rotation.y += angle * Math.PI; // -逆时针，+顺时针
            cube.name = name;
            if(addToScene) {
                scene.add(cube);
            }
            return cube;
        }

        // 墙上挖门窗，通过两个几何体生成BSP对象
        function createResultBSP(srcMesh, destMeshs, addToScene = true) {
            destMeshs = Array.isArray(destMeshs) ? destMeshs : [destMeshs];
            var BSP = new ThreeBSP(srcMesh);
            destMeshs.forEach(destMesh => {
                const destBSP = new ThreeBSP(destMesh);
                // 进行差集计算
                BSP = BSP.subtract(destBSP);
            });
            // 从BSP对象内获取到处理完后的mesh模型数据
            var result = BSP.toMesh(srcMesh.material);
            // 更新模型的面和顶点的数据
            result.geometry.computeFaceNormals();
            result.geometry.computeVertexNormals();
            if(addToScene) {
                scene.add(result);
            }
            return result;
        }

        // 创建库区
        function createArea(x, y, z, width, length, name, color, fontSize, position) {
            var geometry = new THREE.PlaneGeometry(width, length);
            var material = new THREE.MeshLambertMaterial();
            new THREE.TextureLoader().load('../texture/plane.png', function(map) {
                material.map = map;
                material.transparent = true;
                material.opacity = 0.8;
                material.needsUpdate = true;
            });
            var mesh = new THREE.Mesh(geometry, material);
            mesh.position.set(x, y, z);
            mesh.rotation.x = -Math.PI / 2.0;
            mesh.name = name;
            scene.add(mesh);

            new THREE.FontLoader().load("../font/FZYaoTi_Regular.json", function(font) {
                var text = new THREE.TextGeometry(name, {
                    font,
                    size: fontSize,
                    height: 0.01
                });
                text.computeBoundingBox();
                var m = new THREE.MeshStandardMaterial({ color });
                var meshText = new THREE.Mesh(text, m);
                if (position === '左对齐') {
                    meshText.position.x = x - width / 2 + 10;
                } else if (position === "居中"){
                    meshText.position.x = x - 15;
                } else if (position === "右对齐"){
                    meshText.position.x = x + width / 2 - 60;
                }
                meshText.position.y = 1.3;
                meshText.position.z = z + length / 2 - 20;
                meshText.rotation.x = -Math.PI / 2.0;
                scene.add(meshText);
            }); 
        }

        // 创建货架
        function createShelf() {
            var loader = new THREE.FBXLoader();
            loader.load("../model/shelf-free-background-prop/source/Shelf Model V1.fbx", function(object) {
                const pionts = [[0, 0], [0, 180], [0, -180], [700, 0], [700, 180], [700, -180], [-700, 0], [-700, -180], [-700, 180]];
                pionts.forEach(pos => {
                    const shelf = object.clone();
                    shelf.position.x = pos[0];
                    shelf.position.z = pos[1];
                    shelf.scale.multiplyScalar(10);    // 缩放模型大小
                    scene.add(shelf);
                })
            });
        }

        // 创建叉车
        function createForklift(cb) {
            var loader = new THREE.GLTFLoader();
            loader.load("../model/forklift/scene.gltf", function(gltf) {
                // console.log(gltf);
                const forklift = gltf.scene;
                forklift.scale.set(0.8, 0.8, 0.8);
                forklift.position.set(-1100, 0, 500);
                forklift.rotation.y = Math.PI;
                scene.add(forklift);
                cb && cb(forklift);
            });
        }

        // 初始化叉车动画
        function initForkliftAnimation(forklift) {
            // const tween = goForward(-1100, 0, -500, 3000)
            //              .chain(turnRight())
            //              .chain(goForward(-350, 0, 500, 3000))
            //              .chain(turnRight());
            // tween.start();
            return goForward(-1100, 0, -500, 3000)
                .then(() => turn('right'), 1000)
                .then(() => goForward(-350, 0, -500, 3000))
                .then(() => turn('right'), 1000)
                .then(() => goForward(-350, 0, 500, 3000))
                .then(() => turn('left'))
                .then(() => goForward(350, 0, 500, 3000))
                .then(() => turn('left'))
                .then(() => goForward(350, 0, -500, 3000))
                .then(() => turn('right'))
                .then(() => goForward(1100, 0, -500, 3000))
                .then(() => turn('right'))
                .then(() => goForward(1100, 0, 500, 3000))
                .then(() => turn('right'))
                .then(() => goForward(-1100, 0, 500, 6000))
                .then(() => turn('right'))
                .then(() => initForkliftAnimation(forklift));

            function goForward(x, y, z, time) {
                return new Promise(resolve => {
                    const position = {x: forklift.position.x, y: forklift.position.y, z: forklift.position.z};
                    const tween = new TWEEN.Tween(position).to({x, y, z}, time);
                    tween.easing(TWEEN.Easing.Sinusoidal.InOut);
                    tween.onUpdate(() => {
                        forklift.position.set(position.x, position.y, position.z);
                    });
                    tween.onComplete(resolve);
                    tween.start();
                });
            }

            function turn(direction, time) {
                return new Promise(resolve => {
                    const rotation = { y: forklift.rotation.y };
                    const tween = new TWEEN.Tween(rotation).to({
                        y: direction === 'left' ? rotation.y + Math.PI / 2 : rotation.y - Math.PI / 2
                    }, time);
                    tween.easing(TWEEN.Easing.Sinusoidal.InOut);
                    tween.onUpdate(() => {
                        forklift.rotation.y = rotation.y;
                    });
                    tween.onComplete(resolve);
                    tween.start();
                });
            }
        }

        // 初始化轨迹球控件
        function initControls() {
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dampingFactor = 0.5;
            // 视角最小距离
            controls.minDistance = 100;
            // 视角最大距离
            controls.maxDistance = 5000;
            // 最大角度
            controls.maxPolarAngle = Math.PI / 2.2;
        }

        // 初始化性能监控插件
        function initStats() {
            stats = new Stats();
            stats.domElement.style.position = "absolute";
            stats.domElement.style.left = "0px";
            stats.domElement.style.top = "0px";
            document.body.appendChild(stats.domElement);
            return stats;
        }

        // 更新控件
        function update() {
            stats.update();
            controls.update();
            TWEEN.update();
        }

        // 初始化
        function init() {
            initScene();
            initCamera();
            initLight();
            initRenderer();
            initModels();
            initControls();
            initStats();
            window.addEventListener('resize', onWindowResize, false);
        }

        // 窗口大小发生变化
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // 循环更新视图
        function loop() {
            requestAnimationFrame(loop);
            renderer.render(scene, camera);
            update();
        }

        init();
        loop();
    </script>
</body>
</html>